package logic

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"gocode/goland/shortener/internal/svc"
	"gocode/goland/shortener/internal/types"
	"gocode/goland/shortener/model"
	"gocode/goland/shortener/pkg/base62"
	"gocode/goland/shortener/pkg/connect"
	"gocode/goland/shortener/pkg/md5"
	"gocode/goland/shortener/pkg/urltool"
)

type ConvertLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewConvertLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ConvertLogic {
	return &ConvertLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ConvertLogic) Convert(req *types.ConvertRequest) (resp *types.ConvertResponse, err error) {
	// todo: add your logic here and delete this line
	//输入一个长连接->短链接
	//1.检验数据

	//1.1数据不能为空
	//if len(req.LongUrl)==0{}
	//使用validator包来做参数校验

	//1.2长连接能请求通
	if ok := connect.Get(req.LongUrl); !ok {
		return nil, errors.New("无效的链接")
	}

	//1.3判断之前是否已经转过
	//1.3.1 给长连接生成MD5
	md5value := md5.Sum([]byte(req.LongUrl))
	//1.3.2 拿md5去数据库中查是否存在
	u, err := l.svcCtx.ShortUrlModel.FindOneByMd5(l.ctx, sql.NullString{md5value, true})
	if err != sqlx.ErrNotFound {
		if err == nil {
			return nil, fmt.Errorf("该链接已经被转为%s", u.Surl.String)
		}
		logx.Errorw("ShortUrlModel.FindOneByMd5 failed ", logx.Field("err", err))
		return nil, err
	}

	//1.4输入不能是一个短链接（避免循环转）
	//输入的是一个完整的url  解析拿到短链接
	basepath, err := urltool.GetBasePath(req.LongUrl)
	if err != nil {
		logx.Errorw("GetBasePath failed ", logx.Field("err", err))
		return nil, err
	}
	_, err = l.svcCtx.ShortUrlModel.FindOneBySurl(l.ctx, sql.NullString{basepath, true})
	if err != sqlx.ErrNotFound {
		if err == nil {
			return nil, errors.New("已经是短链了")
		}
		logx.Errorw("ShortUrlModel.FindOneBySurl failed ", logx.Field("err", err))
		return nil, err
	}

	var short string
	for {
		//2.取号 基于mysql实现发号器
		//每来一个转链请求，我们就使用 Replace into 语句往sequence表插入一条数据，并且取出主键id作为号码
		seq, err := l.svcCtx.Sequence.Next()
		if err != nil {
			logx.Errorw("Sequence.Next failed ", logx.Field("err", err))
			return nil, err
		}
		fmt.Println("seq:", seq)
		//3.号码转为短链
		//3.1安全性 6347=1En 可以随机给打乱62进制字符的顺序
		short = base62.IntToBase62(seq)
		//3.2避免出现某些特殊词汇  黑名单机制 api、fuck、health等
		if _, ok := l.svcCtx.ShortUrlBlackList[short]; !ok {
			break
		}
	}

	//4.1存储长连接短链接映射关系
	_, err = l.svcCtx.ShortUrlModel.Insert(l.ctx, &model.ShortUrlMap{
		Lurl: sql.NullString{String: req.LongUrl, Valid: true},
		Md5:  sql.NullString{String: md5value, Valid: true},
		Surl: sql.NullString{String: short, Valid: true},
	})
	if err != nil {
		logx.Errorw("ShortUrlModel.Insert failed ", logx.Field("err", err))
		return nil, err
	}
	//4.2将生成的短链接加载到布隆过滤器中
	if err = l.svcCtx.Fliter.Add([]byte(short)); err != nil {
		logx.Errorw("Fliter.Add failed ", logx.Field("err", err))
		return nil, err
	}
	//5.返回响应
	//5.1返回是短域名加短域名加短链接
	shorturl := l.svcCtx.Config.ShortDomains + "/" + short
	return &types.ConvertResponse{ShortUrl: shorturl}, nil
}
